/**
 * re-share an existing share (value) under a new threshold or to a new set of parties or both.
 * Do not use this to refresh a share (use {@link jiff-client~JIFFClient#SecretShare#refresh} instead)
 * @function reshare
 * @ignore
 * @param {module:jiff-client~JIFFClient} jiff - the jiff client instance
 * @param {module:jiff-client~JIFFClient#SecretShare} [share=null] - the share you would like to reshare (null if you are a receiver but not a sender)
 * @param {number} [threshold=receivers_list.length] - the new threshold, defaults to the length of receivers_list param
 * @param {Array} [receivers_list=all_parties] - array of party ids to receive from, by default, this includes all parties
 * @param {Array} [senders_list=all_parties] - array of party ids that posses the share and will reshare it with the receivers, by default, this includes all parties
 * @param {number} [Zp=jiff.Zp] - the Zp of the existing share
 * @param {string} [op_id=auto_gen()] - the operation id which is used to identify this multiplication (and internally, the corresponding beaver triplet).
 *                         This id must be unique, and must be passed by all parties to the same instruction.
 *                         this ensures that every party gets a share from the same triplet for every matching instruction. An automatic id
 *                         is generated by increasing a local counter, default ids suffice when all parties execute the
 *                         instructions in the same order
 * @return {SecretShare} this party's share of the result under the new threshold, or null if this party is not a receiver
 */
module.exports = function (jiff, share, threshold, receivers_list, senders_list, Zp, op_id) {
  var i;

  // default values
  if (receivers_list == null) {
    receivers_list = [];
    for (i = 1; i <= jiff.party_count; i++) {
      receivers_list.push(i);
    }
  } else {
    jiff.helpers.sort_ids(receivers_list);
  }
  if (senders_list == null) {
    senders_list = [];
    for (i = 1; i <= jiff.party_count; i++) {
      senders_list.push(i);
    }
  } else {
    jiff.helpers.sort_ids(senders_list);
  }

  if (threshold == null) {
    threshold = receivers_list.length;
  }
  if (Zp == null) {
    Zp = jiff.Zp;
  }

  if (op_id == null) {
    op_id = jiff.counters.gen_op_id('reshare', senders_list);
  }

  // Check if this party is a sender or receiver
  var isSender = senders_list.indexOf(jiff.id) > -1;
  var isReceiver = receivers_list.indexOf(jiff.id) > -1;
  if (!isSender && !isReceiver) {
    return null;
  }

  // optimization, if nothing changes, keep share
  if (share != null && JSON.stringify(receivers_list) === JSON.stringify(senders_list) && threshold === share.threshold) {
    return share;
  }

  // Setup the result
  var final_deferred;
  var result = null;
  if (isReceiver) {
    final_deferred = new jiff.helpers.Deferred();
    result = new jiff.SecretShare(final_deferred.promise, receivers_list, threshold, Zp);
  }

  // This function is called when the share is ready: the value of the share has been received.
  var ready_share = function () {
    var intermediate_shares = jiff.internal_share(isSender ? share.value : null, threshold, receivers_list, senders_list, Zp, op_id);

    if (isReceiver) {
      var promises = [];
      for (var i = 0; i < senders_list.length; i++) {
        var party_id = senders_list[i];
        promises.push(intermediate_shares[party_id].value);
      }

      // Reconstruct share under new threshold
      Promise.all(promises).then(function () {
        var reconstruct_parts = [];
        for (var i = 0; i < senders_list.length; i++) {
          var party_id = senders_list[i];
          //shamir reconstruct takes an array of objects
          //has attributes: {value: x, sender_id: y, Zp: jiff_instance.Zp}
          reconstruct_parts[i] = {value: intermediate_shares[party_id].value, sender_id: party_id, Zp: Zp};
        }
        var value = jiff.hooks.reconstructShare(jiff, reconstruct_parts);
        final_deferred.resolve(value);
      });
    }
  };

  if (isSender && !share.ready) {
    share.value.then(ready_share);
  } else { // either a receiver or share is ready
    ready_share();
  }

  return result;
};